home *** CD-ROM | disk | FTP | other *** search
/ Personal Computer World 2009 February / PCWFEB09.iso / Software / Linux / Kubuntu 8.10 / kubuntu-8.10-desktop-i386.iso / casper / filesystem.squashfs / usr / lib / python2.5 / smtplib.pyc (.txt) < prev    next >
Python Compiled Bytecode  |  2008-10-29  |  26KB  |  834 lines

  1. # Source Generated with Decompyle++
  2. # File: in.pyc (Python 2.5)
  3.  
  4. '''SMTP/ESMTP client class.
  5.  
  6. This should follow RFC 821 (SMTP), RFC 1869 (ESMTP), RFC 2554 (SMTP
  7. Authentication) and RFC 2487 (Secure SMTP over TLS).
  8.  
  9. Notes:
  10.  
  11. Please remember, when doing ESMTP, that the names of the SMTP service
  12. extensions are NOT the same thing as the option keywords for the RCPT
  13. and MAIL commands!
  14.  
  15. Example:
  16.  
  17.   >>> import smtplib
  18.   >>> s=smtplib.SMTP("localhost")
  19.   >>> print s.help()
  20.   This is Sendmail version 8.8.4
  21.   Topics:
  22.       HELO    EHLO    MAIL    RCPT    DATA
  23.       RSET    NOOP    QUIT    HELP    VRFY
  24.       EXPN    VERB    ETRN    DSN
  25.   For more info use "HELP <topic>".
  26.   To report bugs in the implementation send email to
  27.       sendmail-bugs@sendmail.org.
  28.   For local information send email to Postmaster at your site.
  29.   End of HELP info
  30.   >>> s.putcmd("vrfy","someone@here")
  31.   >>> s.getreply()
  32.   (250, "Somebody OverHere <somebody@here.my.org>")
  33.   >>> s.quit()
  34. '''
  35. import socket
  36. import re
  37. import email.Utils as email
  38. import base64
  39. import hmac
  40. from email.base64MIME import encode as encode_base64
  41. from sys import stderr
  42. __all__ = [
  43.     'SMTPException',
  44.     'SMTPServerDisconnected',
  45.     'SMTPResponseException',
  46.     'SMTPSenderRefused',
  47.     'SMTPRecipientsRefused',
  48.     'SMTPDataError',
  49.     'SMTPConnectError',
  50.     'SMTPHeloError',
  51.     'SMTPAuthenticationError',
  52.     'quoteaddr',
  53.     'quotedata',
  54.     'SMTP']
  55. SMTP_PORT = 25
  56. CRLF = '\r\n'
  57. OLDSTYLE_AUTH = re.compile('auth=(.*)', re.I)
  58.  
  59. class SMTPException(Exception):
  60.     '''Base class for all exceptions raised by this module.'''
  61.     pass
  62.  
  63.  
  64. class SMTPServerDisconnected(SMTPException):
  65.     '''Not connected to any SMTP server.
  66.  
  67.     This exception is raised when the server unexpectedly disconnects,
  68.     or when an attempt is made to use the SMTP instance before
  69.     connecting it to a server.
  70.     '''
  71.     pass
  72.  
  73.  
  74. class SMTPResponseException(SMTPException):
  75.     """Base class for all exceptions that include an SMTP error code.
  76.  
  77.     These exceptions are generated in some instances when the SMTP
  78.     server returns an error code.  The error code is stored in the
  79.     `smtp_code' attribute of the error, and the `smtp_error' attribute
  80.     is set to the error message.
  81.     """
  82.     
  83.     def __init__(self, code, msg):
  84.         self.smtp_code = code
  85.         self.smtp_error = msg
  86.         self.args = (code, msg)
  87.  
  88.  
  89.  
  90. class SMTPSenderRefused(SMTPResponseException):
  91.     """Sender address refused.
  92.  
  93.     In addition to the attributes set by on all SMTPResponseException
  94.     exceptions, this sets `sender' to the string that the SMTP refused.
  95.     """
  96.     
  97.     def __init__(self, code, msg, sender):
  98.         self.smtp_code = code
  99.         self.smtp_error = msg
  100.         self.sender = sender
  101.         self.args = (code, msg, sender)
  102.  
  103.  
  104.  
  105. class SMTPRecipientsRefused(SMTPException):
  106.     """All recipient addresses refused.
  107.  
  108.     The errors for each recipient are accessible through the attribute
  109.     'recipients', which is a dictionary of exactly the same sort as
  110.     SMTP.sendmail() returns.
  111.     """
  112.     
  113.     def __init__(self, recipients):
  114.         self.recipients = recipients
  115.         self.args = (recipients,)
  116.  
  117.  
  118.  
  119. class SMTPDataError(SMTPResponseException):
  120.     """The SMTP server didn't accept the data."""
  121.     pass
  122.  
  123.  
  124. class SMTPConnectError(SMTPResponseException):
  125.     '''Error during connection establishment.'''
  126.     pass
  127.  
  128.  
  129. class SMTPHeloError(SMTPResponseException):
  130.     '''The server refused our HELO reply.'''
  131.     pass
  132.  
  133.  
  134. class SMTPAuthenticationError(SMTPResponseException):
  135.     """Authentication error.
  136.  
  137.     Most probably the server didn't accept the username/password
  138.     combination provided.
  139.     """
  140.     pass
  141.  
  142.  
  143. class SSLFakeSocket:
  144.     '''A fake socket object that really wraps a SSLObject.
  145.  
  146.     It only supports what is needed in smtplib.
  147.     '''
  148.     
  149.     def __init__(self, realsock, sslobj):
  150.         self.realsock = realsock
  151.         self.sslobj = sslobj
  152.  
  153.     
  154.     def send(self, str):
  155.         self.sslobj.write(str)
  156.         return len(str)
  157.  
  158.     sendall = send
  159.     
  160.     def close(self):
  161.         self.realsock.close()
  162.  
  163.  
  164.  
  165. class SSLFakeFile:
  166.     '''A fake file like object that really wraps a SSLObject.
  167.  
  168.     It only supports what is needed in smtplib.
  169.     '''
  170.     
  171.     def __init__(self, sslobj):
  172.         self.sslobj = sslobj
  173.  
  174.     
  175.     def readline(self):
  176.         str = ''
  177.         chr = None
  178.         while chr != '\n':
  179.             chr = self.sslobj.read(1)
  180.             str += chr
  181.         return str
  182.  
  183.     
  184.     def close(self):
  185.         pass
  186.  
  187.  
  188.  
  189. def quoteaddr(addr):
  190.     '''Quote a subset of the email addresses defined by RFC 821.
  191.  
  192.     Should be able to handle anything rfc822.parseaddr can handle.
  193.     '''
  194.     m = (None, None)
  195.     
  196.     try:
  197.         m = email.Utils.parseaddr(addr)[1]
  198.     except AttributeError:
  199.         pass
  200.  
  201.     if m == (None, None):
  202.         return '<%s>' % addr
  203.     elif m is None:
  204.         return '<>'
  205.     else:
  206.         return '<%s>' % m
  207.  
  208.  
  209. def quotedata(data):
  210.     """Quote data for email.
  211.  
  212.     Double leading '.', and change Unix newline '\\n', or Mac '\\r' into
  213.     Internet CRLF end-of-line.
  214.     """
  215.     return re.sub('(?m)^\\.', '..', re.sub('(?:\\r\\n|\\n|\\r(?!\\n))', CRLF, data))
  216.  
  217.  
  218. class SMTP:
  219.     """This class manages a connection to an SMTP or ESMTP server.
  220.     SMTP Objects:
  221.         SMTP objects have the following attributes:
  222.             helo_resp
  223.                 This is the message given by the server in response to the
  224.                 most recent HELO command.
  225.  
  226.             ehlo_resp
  227.                 This is the message given by the server in response to the
  228.                 most recent EHLO command. This is usually multiline.
  229.  
  230.             does_esmtp
  231.                 This is a True value _after you do an EHLO command_, if the
  232.                 server supports ESMTP.
  233.  
  234.             esmtp_features
  235.                 This is a dictionary, which, if the server supports ESMTP,
  236.                 will _after you do an EHLO command_, contain the names of the
  237.                 SMTP service extensions this server supports, and their
  238.                 parameters (if any).
  239.  
  240.                 Note, all extension names are mapped to lower case in the
  241.                 dictionary.
  242.  
  243.         See each method's docstrings for details.  In general, there is a
  244.         method of the same name to perform each SMTP command.  There is also a
  245.         method called 'sendmail' that will do an entire mail transaction.
  246.         """
  247.     debuglevel = 0
  248.     file = None
  249.     helo_resp = None
  250.     ehlo_resp = None
  251.     does_esmtp = 0
  252.     
  253.     def __init__(self, host = '', port = 0, local_hostname = None):
  254.         """Initialize a new instance.
  255.  
  256.         If specified, `host' is the name of the remote host to which to
  257.         connect.  If specified, `port' specifies the port to which to connect.
  258.         By default, smtplib.SMTP_PORT is used.  An SMTPConnectError is raised
  259.         if the specified `host' doesn't respond correctly.  If specified,
  260.         `local_hostname` is used as the FQDN of the local host.  By default,
  261.         the local hostname is found using socket.getfqdn().
  262.  
  263.         """
  264.         self.esmtp_features = { }
  265.         if host:
  266.             (code, msg) = self.connect(host, port)
  267.             if code != 220:
  268.                 raise SMTPConnectError(code, msg)
  269.             
  270.         
  271.         if local_hostname is not None:
  272.             self.local_hostname = local_hostname
  273.         else:
  274.             fqdn = socket.getfqdn()
  275.             if '.' in fqdn:
  276.                 self.local_hostname = fqdn
  277.             else:
  278.                 addr = '127.0.0.1'
  279.                 
  280.                 try:
  281.                     addr = socket.gethostbyname(socket.gethostname())
  282.                 except socket.gaierror:
  283.                     pass
  284.  
  285.                 self.local_hostname = '[%s]' % addr
  286.  
  287.     
  288.     def set_debuglevel(self, debuglevel):
  289.         '''Set the debug output level.
  290.  
  291.         A non-false value results in debug messages for connection and for all
  292.         messages sent to and received from the server.
  293.  
  294.         '''
  295.         self.debuglevel = debuglevel
  296.  
  297.     
  298.     def connect(self, host = 'localhost', port = 0):
  299.         """Connect to a host on a given port.
  300.  
  301.         If the hostname ends with a colon (`:') followed by a number, and
  302.         there is no port specified, that suffix will be stripped off and the
  303.         number interpreted as the port number to use.
  304.  
  305.         Note: This method is automatically invoked by __init__, if a host is
  306.         specified during instantiation.
  307.  
  308.         """
  309.         if not port and host.find(':') == host.rfind(':'):
  310.             i = host.rfind(':')
  311.             if i >= 0:
  312.                 host = host[:i]
  313.                 port = host[i + 1:]
  314.                 
  315.                 try:
  316.                     port = int(port)
  317.                 except ValueError:
  318.                     raise socket.error, 'nonnumeric port'
  319.                 except:
  320.                     None<EXCEPTION MATCH>ValueError
  321.                 
  322.  
  323.             None<EXCEPTION MATCH>ValueError
  324.         
  325.         if not port:
  326.             port = SMTP_PORT
  327.         
  328.         if self.debuglevel > 0:
  329.             print >>stderr, 'connect:', (host, port)
  330.         
  331.         msg = 'getaddrinfo returns an empty list'
  332.         self.sock = None
  333.         for res in socket.getaddrinfo(host, port, 0, socket.SOCK_STREAM):
  334.             (af, socktype, proto, canonname, sa) = res
  335.             
  336.             try:
  337.                 self.sock = socket.socket(af, socktype, proto)
  338.                 if self.debuglevel > 0:
  339.                     print >>stderr, 'connect:', sa
  340.                 
  341.                 self.sock.connect(sa)
  342.             except socket.error:
  343.                 msg = None
  344.                 if self.debuglevel > 0:
  345.                     print >>stderr, 'connect fail:', msg
  346.                 
  347.                 if self.sock:
  348.                     self.sock.close()
  349.                 
  350.                 self.sock = None
  351.                 continue
  352.  
  353.             break
  354.         
  355.         if not self.sock:
  356.             raise socket.error, msg
  357.         
  358.         (code, msg) = self.getreply()
  359.         if self.debuglevel > 0:
  360.             print >>stderr, 'connect:', msg
  361.         
  362.         return (code, msg)
  363.  
  364.     
  365.     def send(self, str):
  366.         """Send `str' to the server."""
  367.         if self.debuglevel > 0:
  368.             print >>stderr, 'send:', repr(str)
  369.         
  370.         if self.sock:
  371.             
  372.             try:
  373.                 self.sock.sendall(str)
  374.             except socket.error:
  375.                 self.close()
  376.                 raise SMTPServerDisconnected('Server not connected')
  377.             except:
  378.                 None<EXCEPTION MATCH>socket.error
  379.             
  380.  
  381.         None<EXCEPTION MATCH>socket.error
  382.         raise SMTPServerDisconnected('please run connect() first')
  383.  
  384.     
  385.     def putcmd(self, cmd, args = ''):
  386.         '''Send a command to the server.'''
  387.         if args == '':
  388.             str = '%s%s' % (cmd, CRLF)
  389.         else:
  390.             str = '%s %s%s' % (cmd, args, CRLF)
  391.         self.send(str)
  392.  
  393.     
  394.     def getreply(self):
  395.         """Get a reply from the server.
  396.  
  397.         Returns a tuple consisting of:
  398.  
  399.           - server response code (e.g. '250', or such, if all goes well)
  400.             Note: returns -1 if it can't read response code.
  401.  
  402.           - server response string corresponding to response code (multiline
  403.             responses are converted to a single, multiline string).
  404.  
  405.         Raises SMTPServerDisconnected if end-of-file is reached.
  406.         """
  407.         resp = []
  408.         if self.file is None:
  409.             self.file = self.sock.makefile('rb')
  410.         
  411.         while None:
  412.             line = self.file.readline()
  413.             if line == '':
  414.                 self.close()
  415.                 raise SMTPServerDisconnected('Connection unexpectedly closed')
  416.             
  417.             if self.debuglevel > 0:
  418.                 print >>stderr, 'reply:', repr(line)
  419.             
  420.             code = line[:3]
  421.             
  422.             try:
  423.                 errcode = int(code)
  424.             except ValueError:
  425.                 errcode = -1
  426.                 break
  427.  
  428.             if line[3:4] != '-':
  429.                 break
  430.                 continue
  431.             continue
  432.             errmsg = '\n'.join(resp)
  433.             if self.debuglevel > 0:
  434.                 print >>stderr, 'reply: retcode (%s); Msg: %s' % (errcode, errmsg)
  435.             
  436.         return (errcode, errmsg)
  437.  
  438.     
  439.     def docmd(self, cmd, args = ''):
  440.         '''Send a command, and return its response code.'''
  441.         self.putcmd(cmd, args)
  442.         return self.getreply()
  443.  
  444.     
  445.     def helo(self, name = ''):
  446.         """SMTP 'helo' command.
  447.         Hostname to send for this command defaults to the FQDN of the local
  448.         host.
  449.         """
  450.         if not name:
  451.             pass
  452.         self.putcmd('helo', self.local_hostname)
  453.         (code, msg) = self.getreply()
  454.         self.helo_resp = msg
  455.         return (code, msg)
  456.  
  457.     
  458.     def ehlo(self, name = ''):
  459.         """ SMTP 'ehlo' command.
  460.         Hostname to send for this command defaults to the FQDN of the local
  461.         host.
  462.         """
  463.         self.esmtp_features = { }
  464.         if not name:
  465.             pass
  466.         self.putcmd('ehlo', self.local_hostname)
  467.         (code, msg) = self.getreply()
  468.         if code == -1 and len(msg) == 0:
  469.             self.close()
  470.             raise SMTPServerDisconnected('Server not connected')
  471.         
  472.         self.ehlo_resp = msg
  473.         if code != 250:
  474.             return (code, msg)
  475.         
  476.         self.does_esmtp = 1
  477.         resp = self.ehlo_resp.split('\n')
  478.         del resp[0]
  479.         for each in resp:
  480.             auth_match = OLDSTYLE_AUTH.match(each)
  481.             if auth_match:
  482.                 self.esmtp_features['auth'] = self.esmtp_features.get('auth', '') + ' ' + auth_match.groups(0)[0]
  483.                 continue
  484.             
  485.             m = re.match('(?P<feature>[A-Za-z0-9][A-Za-z0-9\\-]*) ?', each)
  486.             if m:
  487.                 feature = m.group('feature').lower()
  488.                 params = m.string[m.end('feature'):].strip()
  489.                 if feature == 'auth':
  490.                     self.esmtp_features[feature] = self.esmtp_features.get(feature, '') + ' ' + params
  491.                 else:
  492.                     self.esmtp_features[feature] = params
  493.             feature == 'auth'
  494.         
  495.         return (code, msg)
  496.  
  497.     
  498.     def has_extn(self, opt):
  499.         '''Does the server support a given SMTP service extension?'''
  500.         return opt.lower() in self.esmtp_features
  501.  
  502.     
  503.     def help(self, args = ''):
  504.         """SMTP 'help' command.
  505.         Returns help text from server."""
  506.         self.putcmd('help', args)
  507.         return self.getreply()[1]
  508.  
  509.     
  510.     def rset(self):
  511.         """SMTP 'rset' command -- resets session."""
  512.         return self.docmd('rset')
  513.  
  514.     
  515.     def noop(self):
  516.         """SMTP 'noop' command -- doesn't do anything :>"""
  517.         return self.docmd('noop')
  518.  
  519.     
  520.     def mail(self, sender, options = []):
  521.         """SMTP 'mail' command -- begins mail xfer session."""
  522.         optionlist = ''
  523.         if options and self.does_esmtp:
  524.             optionlist = ' ' + ' '.join(options)
  525.         
  526.         self.putcmd('mail', 'FROM:%s%s' % (quoteaddr(sender), optionlist))
  527.         return self.getreply()
  528.  
  529.     
  530.     def rcpt(self, recip, options = []):
  531.         """SMTP 'rcpt' command -- indicates 1 recipient for this mail."""
  532.         optionlist = ''
  533.         if options and self.does_esmtp:
  534.             optionlist = ' ' + ' '.join(options)
  535.         
  536.         self.putcmd('rcpt', 'TO:%s%s' % (quoteaddr(recip), optionlist))
  537.         return self.getreply()
  538.  
  539.     
  540.     def data(self, msg):
  541.         """SMTP 'DATA' command -- sends message data to server.
  542.  
  543.         Automatically quotes lines beginning with a period per rfc821.
  544.         Raises SMTPDataError if there is an unexpected reply to the
  545.         DATA command; the return value from this method is the final
  546.         response code received when the all data is sent.
  547.         """
  548.         self.putcmd('data')
  549.         (code, repl) = self.getreply()
  550.         if self.debuglevel > 0:
  551.             print >>stderr, 'data:', (code, repl)
  552.         
  553.         if code != 354:
  554.             raise SMTPDataError(code, repl)
  555.         else:
  556.             q = quotedata(msg)
  557.             if q[-2:] != CRLF:
  558.                 q = q + CRLF
  559.             
  560.             q = q + '.' + CRLF
  561.             self.send(q)
  562.             (code, msg) = self.getreply()
  563.             if self.debuglevel > 0:
  564.                 print >>stderr, 'data:', (code, msg)
  565.             
  566.             return (code, msg)
  567.  
  568.     
  569.     def verify(self, address):
  570.         """SMTP 'verify' command -- checks for address validity."""
  571.         self.putcmd('vrfy', quoteaddr(address))
  572.         return self.getreply()
  573.  
  574.     vrfy = verify
  575.     
  576.     def expn(self, address):
  577.         """SMTP 'verify' command -- checks for address validity."""
  578.         self.putcmd('expn', quoteaddr(address))
  579.         return self.getreply()
  580.  
  581.     
  582.     def login(self, user, password):
  583.         """Log in on an SMTP server that requires authentication.
  584.  
  585.         The arguments are:
  586.             - user:     The user name to authenticate with.
  587.             - password: The password for the authentication.
  588.  
  589.         If there has been no previous EHLO or HELO command this session, this
  590.         method tries ESMTP EHLO first.
  591.  
  592.         This method will return normally if the authentication was successful.
  593.  
  594.         This method may raise the following exceptions:
  595.  
  596.          SMTPHeloError            The server didn't reply properly to
  597.                                   the helo greeting.
  598.          SMTPAuthenticationError  The server didn't accept the username/
  599.                                   password combination.
  600.          SMTPException            No suitable authentication method was
  601.                                   found.
  602.         """
  603.         
  604.         def encode_cram_md5(challenge, user, password):
  605.             challenge = base64.decodestring(challenge)
  606.             response = user + ' ' + hmac.HMAC(password, challenge).hexdigest()
  607.             return encode_base64(response, eol = '')
  608.  
  609.         
  610.         def encode_plain(user, password):
  611.             return encode_base64('\x00%s\x00%s' % (user, password), eol = '')
  612.  
  613.         AUTH_PLAIN = 'PLAIN'
  614.         AUTH_CRAM_MD5 = 'CRAM-MD5'
  615.         AUTH_LOGIN = 'LOGIN'
  616.         if self.helo_resp is None and self.ehlo_resp is None:
  617.             if self.ehlo()[0] <= self.ehlo()[0]:
  618.                 pass
  619.             elif not self.ehlo()[0] <= 299:
  620.                 (code, resp) = self.helo()
  621.                 if code <= code:
  622.                     pass
  623.                 elif not code <= 299:
  624.                     raise SMTPHeloError(code, resp)
  625.                 
  626.             
  627.         
  628.         if not self.has_extn('auth'):
  629.             raise SMTPException('SMTP AUTH extension not supported by server.')
  630.         
  631.         authlist = self.esmtp_features['auth'].split()
  632.         preferred_auths = [
  633.             AUTH_CRAM_MD5,
  634.             AUTH_PLAIN,
  635.             AUTH_LOGIN]
  636.         authmethod = None
  637.         for method in preferred_auths:
  638.             if method in authlist:
  639.                 authmethod = method
  640.                 break
  641.                 continue
  642.         
  643.         if authmethod == AUTH_CRAM_MD5:
  644.             (code, resp) = self.docmd('AUTH', AUTH_CRAM_MD5)
  645.             if code == 503:
  646.                 return (code, resp)
  647.             
  648.             (code, resp) = self.docmd(encode_cram_md5(resp, user, password))
  649.         elif authmethod == AUTH_PLAIN:
  650.             (code, resp) = self.docmd('AUTH', AUTH_PLAIN + ' ' + encode_plain(user, password))
  651.         elif authmethod == AUTH_LOGIN:
  652.             (code, resp) = self.docmd('AUTH', '%s %s' % (AUTH_LOGIN, encode_base64(user, eol = '')))
  653.             if code != 334:
  654.                 raise SMTPAuthenticationError(code, resp)
  655.             
  656.             (code, resp) = self.docmd(encode_base64(password, eol = ''))
  657.         elif authmethod is None:
  658.             raise SMTPException('No suitable authentication method found.')
  659.         
  660.         if code not in (235, 503):
  661.             raise SMTPAuthenticationError(code, resp)
  662.         
  663.         return (code, resp)
  664.  
  665.     
  666.     def starttls(self, keyfile = None, certfile = None):
  667.         '''Puts the connection to the SMTP server into TLS mode.
  668.  
  669.         If the server supports TLS, this will encrypt the rest of the SMTP
  670.         session. If you provide the keyfile and certfile parameters,
  671.         the identity of the SMTP server and client can be checked. This,
  672.         however, depends on whether the socket module really checks the
  673.         certificates.
  674.         '''
  675.         (resp, reply) = self.docmd('STARTTLS')
  676.         if resp == 220:
  677.             sslobj = socket.ssl(self.sock, keyfile, certfile)
  678.             self.sock = SSLFakeSocket(self.sock, sslobj)
  679.             self.file = SSLFakeFile(sslobj)
  680.             self.helo_resp = None
  681.             self.ehlo_resp = None
  682.             self.esmtp_features = { }
  683.             self.does_esmtp = 0
  684.         
  685.         return (resp, reply)
  686.  
  687.     
  688.     def sendmail(self, from_addr, to_addrs, msg, mail_options = [], rcpt_options = []):
  689.         '''This command performs an entire mail transaction.
  690.  
  691.         The arguments are:
  692.             - from_addr    : The address sending this mail.
  693.             - to_addrs     : A list of addresses to send this mail to.  A bare
  694.                              string will be treated as a list with 1 address.
  695.             - msg          : The message to send.
  696.             - mail_options : List of ESMTP options (such as 8bitmime) for the
  697.                              mail command.
  698.             - rcpt_options : List of ESMTP options (such as DSN commands) for
  699.                              all the rcpt commands.
  700.  
  701.         If there has been no previous EHLO or HELO command this session, this
  702.         method tries ESMTP EHLO first.  If the server does ESMTP, message size
  703.         and each of the specified options will be passed to it.  If EHLO
  704.         fails, HELO will be tried and ESMTP options suppressed.
  705.  
  706.         This method will return normally if the mail is accepted for at least
  707.         one recipient.  It returns a dictionary, with one entry for each
  708.         recipient that was refused.  Each entry contains a tuple of the SMTP
  709.         error code and the accompanying error message sent by the server.
  710.  
  711.         This method may raise the following exceptions:
  712.  
  713.          SMTPHeloError          The server didn\'t reply properly to
  714.                                 the helo greeting.
  715.          SMTPRecipientsRefused  The server rejected ALL recipients
  716.                                 (no mail was sent).
  717.          SMTPSenderRefused      The server didn\'t accept the from_addr.
  718.          SMTPDataError          The server replied with an unexpected
  719.                                 error code (other than a refusal of
  720.                                 a recipient).
  721.  
  722.         Note: the connection will be open even after an exception is raised.
  723.  
  724.         Example:
  725.  
  726.          >>> import smtplib
  727.          >>> s=smtplib.SMTP("localhost")
  728.          >>> tolist=["one@one.org","two@two.org","three@three.org","four@four.org"]
  729.          >>> msg = \'\'\'\\
  730.          ... From: Me@my.org
  731.          ... Subject: testin\'...
  732.          ...
  733.          ... This is a test \'\'\'
  734.          >>> s.sendmail("me@my.org",tolist,msg)
  735.          { "three@three.org" : ( 550 ,"User unknown" ) }
  736.          >>> s.quit()
  737.  
  738.         In the above example, the message was accepted for delivery to three
  739.         of the four addresses, and one was rejected, with the error code
  740.         550.  If all addresses are accepted, then the method will return an
  741.         empty dictionary.
  742.  
  743.         '''
  744.         if self.helo_resp is None and self.ehlo_resp is None:
  745.             if self.ehlo()[0] <= self.ehlo()[0]:
  746.                 pass
  747.             elif not self.ehlo()[0] <= 299:
  748.                 (code, resp) = self.helo()
  749.                 if code <= code:
  750.                     pass
  751.                 elif not code <= 299:
  752.                     raise SMTPHeloError(code, resp)
  753.                 
  754.             
  755.         
  756.         esmtp_opts = []
  757.         if self.does_esmtp:
  758.             if self.has_extn('size'):
  759.                 esmtp_opts.append('size=%d' % len(msg))
  760.             
  761.             for option in mail_options:
  762.                 esmtp_opts.append(option)
  763.             
  764.         
  765.         (code, resp) = self.mail(from_addr, esmtp_opts)
  766.         if code != 250:
  767.             self.rset()
  768.             raise SMTPSenderRefused(code, resp, from_addr)
  769.         
  770.         senderrs = { }
  771.         if isinstance(to_addrs, basestring):
  772.             to_addrs = [
  773.                 to_addrs]
  774.         
  775.         for each in to_addrs:
  776.             (code, resp) = self.rcpt(each, rcpt_options)
  777.             if code != 250 and code != 251:
  778.                 senderrs[each] = (code, resp)
  779.                 continue
  780.         
  781.         if len(senderrs) == len(to_addrs):
  782.             self.rset()
  783.             raise SMTPRecipientsRefused(senderrs)
  784.         
  785.         (code, resp) = self.data(msg)
  786.         if code != 250:
  787.             self.rset()
  788.             raise SMTPDataError(code, resp)
  789.         
  790.         return senderrs
  791.  
  792.     
  793.     def close(self):
  794.         '''Close the connection to the SMTP server.'''
  795.         if self.file:
  796.             self.file.close()
  797.         
  798.         self.file = None
  799.         if self.sock:
  800.             self.sock.close()
  801.         
  802.         self.sock = None
  803.  
  804.     
  805.     def quit(self):
  806.         '''Terminate the SMTP session.'''
  807.         self.docmd('quit')
  808.         self.close()
  809.  
  810.  
  811. if __name__ == '__main__':
  812.     import sys
  813.     
  814.     def prompt(prompt):
  815.         sys.stdout.write(prompt + ': ')
  816.         return sys.stdin.readline().strip()
  817.  
  818.     fromaddr = prompt('From')
  819.     toaddrs = prompt('To').split(',')
  820.     print 'Enter message, end with ^D:'
  821.     msg = ''
  822.     while None:
  823.         line = sys.stdin.readline()
  824.         if not line:
  825.             break
  826.         
  827.         msg = msg + line
  828.         continue
  829.         print 'Message length is %d' % len(msg)
  830.         server = SMTP('localhost')
  831.         server.sendmail(fromaddr, toaddrs, msg)
  832.         server.quit()
  833. __name__ == '__main__'
  834.